package de.fhin.mas.agents.manager.behaviours;

import jade.core.behaviours.Behaviour;
import jade.core.behaviours.FSMBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.domain.FIPANames;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import de.fhin.mas.MASEvents;
import de.fhin.mas.agents.manager.ManagerAgent;
import de.fhin.mas.util.MessageUtil;

/**
 * Fortsetzen eines mit system_stop_required pausierten Auftrags initialisieren.
 */
public class InitResume extends FSMBehaviour {
	private static final long serialVersionUID = 4861189157897483221L;
	private final ManagerAgent managerAgent;

	/**
	 * Versand des CFPs
	 */
	private class SendResumeCallBv extends Behaviour {
		private static final long serialVersionUID = -3120548753467738788L;

		private Integer doTransition;

		@Override
		public void action() {
			String Content = "User wants a Resume";

			// "newOrder" Message = CFP Message
			ACLMessage resume = new ACLMessage(ACLMessage.CFP);
			resume.setContent(Content);
			resume.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
			MessageUtil.setMessageTopic(managerAgent, resume, MASEvents.RESUME);

			// Abschicken der Nachricht
			managerAgent.sendToAll(resume);

			// Auf in den nächsten Zustand!
			doTransition = 0;
		}

		@Override
		public int onEnd() {
			// Transistion als Ergebnis liefern und wieder auf null
			// setzen, damit das Behaviour/Zustand wieder auf done=false ist
			int result = doTransition;
			doTransition = null;
			return result;
		}

		@Override
		public boolean done() {
			return doTransition != null;
		}
	}

	/**
	 * Warten auf Proposals.
	 */
	private class WaitForProposalsBv extends Behaviour {
		private static final long serialVersionUID = -3061702920955586325L;

		private int receivedProposes = 0;
		private Integer doTransition = null;

		// private final Date startTime = new Date();

		@Override
		public void action() {
			MessageTemplate mt = MessageTemplate.and(MessageTemplate.MatchConversationId(MASEvents.RESUME.getName()),
					MessageTemplate.or(MessageTemplate.MatchPerformative(ACLMessage.PROPOSE), MessageTemplate
							.MatchPerformative(ACLMessage.REFUSE)));

			managerAgent.logInfo("resume: Waiting for PROPOSE or REFUSE or a timeout!");

			// für Testzwecke ohne Timeout!
			/*
			 * if (new Date().getTime() - startTime.getTime() > 20000) {
			 * doTransition = 0; System.out.println("Timeout"); }
			 */

			ACLMessage msg = myAgent.receive(mt);
			if (msg == null)
				block();
			else {
				if (msg.getPerformative() == ACLMessage.PROPOSE) {
					receivedProposes++;
					if (receivedProposes >= 3)
						doTransition = 1;
				} else if (msg.getPerformative() == ACLMessage.REFUSE)
					doTransition = 0;
			}
		}

		@Override
		public boolean done() {
			return doTransition != null;
		}

		@Override
		public int onEnd() {
			if (doTransition == 0) {
				managerAgent.logInfo("resume: Transition to resume_canceled");

				ACLMessage msg = new ACLMessage(ACLMessage.REJECT_PROPOSAL);
				msg.setConversationId(MASEvents.RESUME.getName());
				msg.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
				managerAgent.sendToAll(msg);

			} else if (doTransition == 1) {
				managerAgent.logInfo("resume: Transition to resume_approved");

				ACLMessage msg = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
				msg.setConversationId(MASEvents.RESUME.getName());
				msg.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
				managerAgent.sendToAll(msg);
			}

			// Transistion als Ergebnis liefern und wieder auf null
			// setzen, damit das Behaviour/Zustand wieder auf done=false ist
			int result = doTransition;
			doTransition = null;
			return result;
		}
	}

	/**
	 * Resume wurde ausgeführt.
	 */
	private class ResumeExecutedBv extends OneShotBehaviour {
		private static final long serialVersionUID = 8171250486308325700L;

		@Override
		public void action() {
			managerAgent.logInfo("resume: State order_executed reached.");
		}
	}

	private class ResumeApprovedBv extends Behaviour {
		private static final long serialVersionUID = 5173460094433947202L;

		private Integer doTransition = null;

		@Override
		public void action() {
			MessageTemplate mt = MessageTemplate.and(MessageTemplate.MatchConversationId(MASEvents.RESUME.getName()),
					MessageTemplate.or(MessageTemplate.MatchPerformative(ACLMessage.FAILURE), MessageTemplate
							.MatchPerformative(ACLMessage.INFORM)));

			while (true) {
				ACLMessage msg = myAgent.receive(mt);
				if (msg == null)
					block();
				else {
					if (msg.getPerformative() == ACLMessage.FAILURE)
						doTransition = 0;
					else if (msg.getPerformative() == ACLMessage.INFORM)
						doTransition = 1;
				}
				if (done())
					break;
			}
		}

		@Override
		public boolean done() {
			return doTransition != null;
		}

		@Override
		public int onEnd() {
			// TODO: Auftrag bei allen abbrechen
			if (doTransition == 0) {
				managerAgent.logInfo("resume: Transition to resume_failed");

			} else if (doTransition == 1) {
				managerAgent.logInfo("resume: Transition to resume_executed");
			}

			// Transistion als Ergebnis liefern und wieder auf null
			// setzen, damit das Behaviour/Zustand wieder auf done=false ist
			int result = doTransition;
			doTransition = null;
			return result;
		}
	}

	/**
	 * Resume fehlgeschlagen.
	 */
	private class ResumeFailedBv extends OneShotBehaviour {
		private static final long serialVersionUID = -4714261118949209630L;

		@Override
		public void action() {
			managerAgent.logInfo("resume: State resume_failed reached.");
		}
	}

	public InitResume(ManagerAgent a) {
		super(a);

		managerAgent = a;

		registerFirstState(new SendResumeCallBv(), "resume_init");
		registerState(new WaitForProposalsBv(), "wait_for_proposals");
		registerState(new ResumeApprovedBv(), "resume_approved");
		registerLastState(new ResumeFailedBv(), "resume_failed");
		registerLastState(new ResumeExecutedBv(), "resume_executed");

		registerTransition("resume_init", "wait_for_proposals", 0);
		registerTransition("wait_for_proposals", "resume_failed", 0);
		registerTransition("wait_for_proposals", "resume_approved", 1);
		registerTransition("resume_approved", "resume_failed", 0);
		registerTransition("resume_approved", "resume_executed", 1);
	}

}
